#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import os
import cv2
import numpy as np
import time
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QPixmap, QImage, QFont
from PyQt5.QtCore import QTimer, pyqtSignal, QThread, Qt

# 添加上级目录到Python路径以导入MvImport模块
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

from MvImport.MvCameraControl_class import *
from MvImport.MvErrorDefine_const import *
from MvImport.PixelType_header import *

class CameraThread(QThread):
    """相机采集线程"""
    image_ready = pyqtSignal(np.ndarray)
    exposure_changed = pyqtSignal(float)
    
    def __init__(self, camera_index=0):
        super().__init__()
        self.camera_index = camera_index
        self.is_running = False
        self.cam = None
        self.current_exposure = 10000.0  # 默认曝光时间10ms
        
    def run(self):
        """运行相机采集"""
        try:
            # 枚举设备
            deviceList = MV_CC_DEVICE_INFO_LIST()
            tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
            
            ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
            if ret != 0 or deviceList.nDeviceNum == 0:
                print("没有找到设备!")
                return
                
            if self.camera_index >= deviceList.nDeviceNum:
                print(f"设备索引 {self.camera_index} 超出范围!")
                return
            
            # 创建相机实例
            self.cam = MvCamera()
            
            # 选择设备并创建句柄
            stDeviceList = cast(deviceList.pDeviceInfo[self.camera_index], POINTER(MV_CC_DEVICE_INFO)).contents
            
            ret = self.cam.MV_CC_CreateHandle(stDeviceList)
            if ret != 0:
                print(f"创建句柄失败! ret[0x{ret:08x}]")
                return
                
            # 打开设备
            ret = self.cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
            if ret != 0:
                print(f"打开设备失败! ret[0x{ret:08x}]")
                self.cam.MV_CC_DestroyHandle()
                return
            
            # 设置触发模式为off
            ret = self.cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
            if ret != 0:
                print(f"设置触发模式失败! ret[0x{ret:08x}]")
            
            # 获取曝光时间范围
            self.get_exposure_range()
            
            # 设置初始曝光时间
            self.set_exposure_time(self.current_exposure)
            
            # 开始取流
            ret = self.cam.MV_CC_StartGrabbing()
            if ret != 0:
                print(f"开始取流失败! ret[0x{ret:08x}]")
                self.cam.MV_CC_CloseDevice()
                self.cam.MV_CC_DestroyHandle()
                return
            
            print("相机开始采集...")
            self.is_running = True
            
            # 获取图像数据
            stOutFrame = MV_FRAME_OUT()
            memset(byref(stOutFrame), 0, sizeof(stOutFrame))
            
            while self.is_running:
                ret = self.cam.MV_CC_GetImageBuffer(stOutFrame, 1000)
                if ret == 0:
                    # 转换图像数据
                    data = (c_ubyte * stOutFrame.stFrameInfo.nFrameLen)()
                    cdll.msvcrt.memcpy(byref(data), stOutFrame.pBufAddr, stOutFrame.stFrameInfo.nFrameLen)
                    
                    # 根据像素格式转换图像
                    if stOutFrame.stFrameInfo.enPixelType == PixelType_Gvsp_Mono8:
                        # 单色图像
                        img_data = np.frombuffer(data, dtype=np.uint8)
                        img = img_data.reshape((stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nWidth))
                        img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
                    elif stOutFrame.stFrameInfo.enPixelType == PixelType_Gvsp_RGB8_Packed:
                        # RGB图像
                        img_data = np.frombuffer(data, dtype=np.uint8)
                        img = img_data.reshape((stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nWidth, 3))
                        img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
                    else:
                        # 其他格式，尝试转换为BGR
                        img_data = np.frombuffer(data, dtype=np.uint8)
                        img = img_data.reshape((stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nWidth, -1))
                        if len(img.shape) == 2:
                            img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
                    
                    # 发送图像信号
                    self.image_ready.emit(img)
                    
                    # 释放图像缓存
                    self.cam.MV_CC_FreeImageBuffer(stOutFrame)
                else:
                    time.sleep(0.01)
                    
        except Exception as e:
            print(f"相机采集错误: {e}")
        finally:
            self.stop()
    
    def get_exposure_range(self):
        """获取曝光时间范围"""
        try:
            # 获取曝光时间最小值
            stFloatParam = MVCC_FLOATVALUE()
            memset(byref(stFloatParam), 0, sizeof(MVCC_FLOATVALUE))
            ret = self.cam.MV_CC_GetFloatValue("ExposureTime", stFloatParam)
            if ret == 0:
                print(f"当前曝光时间: {stFloatParam.fCurValue:.2f} μs")
                print(f"曝光时间最小值: {stFloatParam.fMin:.2f} μs")
                print(f"曝光时间最大值: {stFloatParam.fMax:.2f} μs")
                return stFloatParam.fMin, stFloatParam.fMax
        except Exception as e:
            print(f"获取曝光范围失败: {e}")
        return 1000.0, 100000.0  # 默认范围
    
    def set_exposure_time(self, exposure_time):
        """设置曝光时间"""
        try:
            ret = self.cam.MV_CC_SetFloatValue("ExposureTime", exposure_time)
            if ret == 0:
                self.current_exposure = exposure_time
                print(f"曝光时间设置成功: {exposure_time:.2f} μs")
                self.exposure_changed.emit(exposure_time)
                return True
            else:
                print(f"曝光时间设置失败! ret[0x{ret:08x}]")
                return False
        except Exception as e:
            print(f"设置曝光时间错误: {e}")
            return False
    
    def stop(self):
        """停止采集"""
        self.is_running = False
        if self.cam:
            try:
                self.cam.MV_CC_StopGrabbing()
                self.cam.MV_CC_CloseDevice()
                self.cam.MV_CC_DestroyHandle()
            except:
                pass

class CameraWithExposureControl(QMainWindow):
    """带曝光控制的相机窗口"""
    
    def __init__(self):
        super().__init__()
        self.camera_thread = None
        self.exposure_min = 1000.0
        self.exposure_max = 100000.0
        self.init_ui()
        
    def init_ui(self):
        """初始化界面"""
        self.setWindowTitle('海康威视相机 - 曝光控制')
        self.setGeometry(100, 100, 1200, 800)
        
        # 创建中央部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 创建主布局
        main_layout = QHBoxLayout(central_widget)
        
        # 左侧：图像显示区域
        left_widget = QWidget()
        left_layout = QVBoxLayout(left_widget)
        
        # 图像显示标签
        self.image_label = QLabel()
        self.image_label.setMinimumSize(800, 600)
        self.image_label.setStyleSheet("border: 2px solid gray;")
        self.image_label.setText("等待连接相机...")
        self.image_label.setAlignment(Qt.AlignCenter)
        left_layout.addWidget(self.image_label)
        
        main_layout.addWidget(left_widget)
        
        # 右侧：控制面板
        right_widget = QWidget()
        right_widget.setMaximumWidth(350)
        right_layout = QVBoxLayout(right_widget)
        
        # 标题
        title_label = QLabel("相机控制面板")
        title_label.setFont(QFont("Arial", 14, QFont.Bold))
        title_label.setAlignment(Qt.AlignCenter)
        right_layout.addWidget(title_label)
        
        # 相机控制按钮
        camera_group = QGroupBox("相机控制")
        camera_layout = QVBoxLayout(camera_group)
        
        self.start_button = QPushButton("开始采集")
        self.start_button.setMinimumHeight(40)
        self.start_button.clicked.connect(self.start_camera)
        camera_layout.addWidget(self.start_button)
        
        self.stop_button = QPushButton("停止采集")
        self.stop_button.setMinimumHeight(40)
        self.stop_button.clicked.connect(self.stop_camera)
        self.stop_button.setEnabled(False)
        camera_layout.addWidget(self.stop_button)
        
        right_layout.addWidget(camera_group)
        
        # 曝光控制
        exposure_group = QGroupBox("曝光控制")
        exposure_layout = QVBoxLayout(exposure_group)
        
        # 曝光时间显示
        self.exposure_label = QLabel("曝光时间: 10000.00 μs")
        self.exposure_label.setFont(QFont("Arial", 10))
        exposure_layout.addWidget(self.exposure_label)
        
        # 曝光时间滑块
        self.exposure_slider = QSlider(Qt.Horizontal)
        self.exposure_slider.setMinimum(0)
        self.exposure_slider.setMaximum(1000)
        self.exposure_slider.setValue(100)  # 默认值
        self.exposure_slider.setEnabled(False)
        self.exposure_slider.valueChanged.connect(self.on_exposure_slider_changed)
        exposure_layout.addWidget(self.exposure_slider)
        
        # 曝光时间输入框
        exposure_input_layout = QHBoxLayout()
        exposure_input_layout.addWidget(QLabel("曝光时间(μs):"))
        
        self.exposure_input = QLineEdit("10000")
        self.exposure_input.setEnabled(False)
        self.exposure_input.returnPressed.connect(self.on_exposure_input_changed)
        exposure_input_layout.addWidget(self.exposure_input)
        
        self.set_exposure_button = QPushButton("设置")
        self.set_exposure_button.setEnabled(False)
        self.set_exposure_button.clicked.connect(self.on_exposure_input_changed)
        exposure_input_layout.addWidget(self.set_exposure_button)
        
        exposure_layout.addLayout(exposure_input_layout)
        
        # 预设曝光按钮
        preset_layout = QHBoxLayout()
        
        self.preset_short = QPushButton("短曝光")
        self.preset_short.setEnabled(False)
        self.preset_short.clicked.connect(lambda: self.set_preset_exposure(5000))
        preset_layout.addWidget(self.preset_short)
        
        self.preset_medium = QPushButton("中等曝光")
        self.preset_medium.setEnabled(False)
        self.preset_medium.clicked.connect(lambda: self.set_preset_exposure(20000))
        preset_layout.addWidget(self.preset_medium)
        
        self.preset_long = QPushButton("长曝光")
        self.preset_long.setEnabled(False)
        self.preset_long.clicked.connect(lambda: self.set_preset_exposure(50000))
        preset_layout.addWidget(self.preset_long)
        
        exposure_layout.addLayout(preset_layout)
        
        right_layout.addWidget(exposure_group)
        
        # 图像信息
        info_group = QGroupBox("图像信息")
        info_layout = QVBoxLayout(info_group)
        
        self.info_label = QLabel("图像尺寸: --")
        info_layout.addWidget(self.info_label)
        
        self.fps_label = QLabel("FPS: --")
        info_layout.addWidget(self.fps_label)
        
        right_layout.addWidget(info_group)
        
        # 状态信息
        status_group = QGroupBox("系统状态")
        status_layout = QVBoxLayout(status_group)
        
        self.status_label = QLabel("就绪")
        status_layout.addWidget(self.status_label)
        
        right_layout.addWidget(status_group)
        
        # 添加弹性空间
        right_layout.addStretch()
        
        main_layout.addWidget(right_widget)
        
        self.current_image = None
        self.frame_count = 0
        self.last_fps_time = time.time()
        
    def start_camera(self):
        """开始相机采集"""
        if self.camera_thread is None or not self.camera_thread.is_running:
            self.camera_thread = CameraThread(0)
            self.camera_thread.image_ready.connect(self.update_image)
            self.camera_thread.exposure_changed.connect(self.update_exposure_display)
            self.camera_thread.start()
            
            self.start_button.setEnabled(False)
            self.stop_button.setEnabled(True)
            self.exposure_slider.setEnabled(True)
            self.exposure_input.setEnabled(True)
            self.set_exposure_button.setEnabled(True)
            self.preset_short.setEnabled(True)
            self.preset_medium.setEnabled(True)
            self.preset_long.setEnabled(True)
            self.status_label.setText("正在采集...")
    
    def stop_camera(self):
        """停止相机采集"""
        if self.camera_thread and self.camera_thread.is_running:
            self.camera_thread.stop()
            self.camera_thread.wait()
            
            self.start_button.setEnabled(True)
            self.stop_button.setEnabled(False)
            self.exposure_slider.setEnabled(False)
            self.exposure_input.setEnabled(False)
            self.set_exposure_button.setEnabled(False)
            self.preset_short.setEnabled(False)
            self.preset_medium.setEnabled(False)
            self.preset_long.setEnabled(False)
            self.status_label.setText("已停止")
    
    def update_image(self, img):
        """更新图像显示"""
        self.current_image = img
        
        # 确保图像是3通道的
        if len(img.shape) == 2:
            img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        
        # 调整图像大小以适应显示区域
        height, width, channel = img.shape
        max_width = self.image_label.width() - 10
        max_height = self.image_label.height() - 10
        
        if width > max_width or height > max_height:
            scale = min(max_width / width, max_height / height)
            new_width = int(width * scale)
            new_height = int(height * scale)
            img = cv2.resize(img, (new_width, new_height))
            height, width, channel = img.shape
        
        # 转换为QImage
        bytes_per_line = 3 * width
        q_img = QImage(img.data, width, height, bytes_per_line, QImage.Format_RGB888)
        
        # 显示图像
        pixmap = QPixmap.fromImage(q_img)
        self.image_label.setPixmap(pixmap)
        
        # 更新图像信息
        self.info_label.setText(f"图像尺寸: {width}×{height}")
        
        # 计算FPS
        self.frame_count += 1
        current_time = time.time()
        if current_time - self.last_fps_time >= 1.0:
            fps = self.frame_count / (current_time - self.last_fps_time)
            self.fps_label.setText(f"FPS: {fps:.1f}")
            self.frame_count = 0
            self.last_fps_time = current_time
    
    def update_exposure_display(self, exposure_time):
        """更新曝光时间显示"""
        self.exposure_label.setText(f"曝光时间: {exposure_time:.2f} μs")
        self.exposure_input.setText(f"{exposure_time:.0f}")
        
        # 更新滑块位置
        exposure_range = self.exposure_max - self.exposure_min
        if exposure_range > 0:
            slider_value = int((exposure_time - self.exposure_min) / exposure_range * 1000)
            self.exposure_slider.setValue(slider_value)
    
    def on_exposure_slider_changed(self, value):
        """曝光滑块改变事件"""
        if self.camera_thread and self.camera_thread.is_running:
            # 计算曝光时间
            exposure_range = self.exposure_max - self.exposure_min
            exposure_time = self.exposure_min + (value / 1000.0) * exposure_range
            
            # 设置曝光时间
            self.camera_thread.set_exposure_time(exposure_time)
    
    def on_exposure_input_changed(self):
        """曝光输入框改变事件"""
        if self.camera_thread and self.camera_thread.is_running:
            try:
                exposure_time = float(self.exposure_input.text())
                if self.exposure_min <= exposure_time <= self.exposure_max:
                    self.camera_thread.set_exposure_time(exposure_time)
                else:
                    QMessageBox.warning(self, "警告", f"曝光时间必须在 {self.exposure_min:.0f} - {self.exposure_max:.0f} μs 范围内")
            except ValueError:
                QMessageBox.warning(self, "错误", "请输入有效的数字")
    
    def set_preset_exposure(self, exposure_time):
        """设置预设曝光时间"""
        if self.camera_thread and self.camera_thread.is_running:
            self.camera_thread.set_exposure_time(exposure_time)
    
    def closeEvent(self, event):
        """关闭事件"""
        self.stop_camera()
        event.accept()

def main():
    app = QApplication(sys.argv)
    window = CameraWithExposureControl()
    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main() 